/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    D12.c

Abstract:

    USB device driver for Philips D12 USB test board

Environment:

    kernel mode only

Notes:

  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  PURPOSE.

  Copyright (c) 1996 Microsoft Corporation.  All Rights Reserved.


Revision History:

    5-4-96 : created

--*/

#define DRIVER

#include "wdm.h"
#include "stdarg.h"
#include "stdio.h"

#include "usbdi.h"
#include "usbdlib.h"
#include "D12.h"
#include "d12irp.h"
#include "guid829.h"

//
// Global pointer to Driver Object
//

PDRIVER_OBJECT D12_DriverObject;

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:

    Installable driver initialization entry point.
    This entry point is called directly by the I/O system.

Arguments:

    DriverObject - pointer to the driver object

    RegistryPath - pointer to a unicode string representing the path
                   to driver-specific key in the registry

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_OBJECT deviceObject = NULL;

    D12_KdPrint (("D12TEST.SYS: entering (D12) DriverEntry\n"));

    D12_DriverObject = DriverObject;

    //
    // Create dispatch points for device control, create, close.
    //

    DriverObject->MajorFunction[IRP_MJ_CREATE] = D12_Create;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = D12_Close;
    DriverObject->DriverUnload = D12_Unload;

    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = D12_ProcessIOCTL;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = D12_Write;
    DriverObject->MajorFunction[IRP_MJ_READ] = D12_Read;

    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
    DriverObject->MajorFunction[IRP_MJ_PNP] = D12_Dispatch;
    DriverObject->MajorFunction[IRP_MJ_POWER] = D12_ProcessPowerIrp;
    DriverObject->DriverExtension->AddDevice = D12_PnPAddDevice;

    D12_KdPrint (("D12TEST.SYS: exiting (D12) DriverEntry (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
D12_PoRequestCompletion(
    IN PDEVICE_OBJECT       DeviceObject,
    IN UCHAR                MinorFunction,
    IN POWER_STATE          PowerState,
    IN PVOID                Context,
    IN PIO_STATUS_BLOCK     IoStatus
    )
/*++

Routine Description:

    This routine is called when the port driver completes an IRP.

Arguments:

    DeviceObject - Pointer to the device object for the class device.

    Context - Driver defined context.

Return Value:

    The function value is the final status from the operation.

--*/
{
    PIRP irp;
    PDEVICE_EXTENSION deviceExtension;
    PDEVICE_OBJECT deviceObject = Context;
    NTSTATUS ntStatus;

    deviceExtension = deviceObject->DeviceExtension;
    irp = deviceExtension->PowerIrp;
    
    ntStatus = IoStatus->Status;

    D12_KdPrint(("'D12_PoRequestCompletion\n"));
    
    IoCopyCurrentIrpStackLocationToNext(irp);      
    PoStartNextPowerIrp(irp);
    PoCallDriver(deviceExtension->TopOfStackDeviceObject,
                 irp);   

    D12_DecrementIoCount(deviceObject);                 

    return ntStatus;
}


NTSTATUS
D12_PowerIrp_Complete(
    IN PDEVICE_OBJECT NullDeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
/*++

Routine Description:

    This routine is called when the port driver completes an IRP.


Arguments:

    DeviceObject - Pointer to the device object for the class device.

    Irp - Irp completed.

    Context - Driver defined context.

Return Value:

    The function value is the final status from the operation.

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_OBJECT deviceObject;
    PIO_STACK_LOCATION irpStack;
    PDEVICE_EXTENSION deviceExtension;

    D12_KdPrint(("D12TEST.SYS:   enter D12_PowerIrp_Complete\n"));

    deviceObject = (PDEVICE_OBJECT) Context;

    deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;

    // BUGBUG
    // kenray sez we should be calling IoMarkIrpPending
    // from our completion routine.
    //

    if (Irp->PendingReturned) {
        IoMarkIrpPending(Irp);
    }

    irpStack = IoGetCurrentIrpStackLocation (Irp);

    ASSERT(irpStack->MajorFunction == IRP_MJ_POWER);
    ASSERT(irpStack->MinorFunction == IRP_MN_SET_POWER);
    ASSERT(irpStack->Parameters.Power.Type==DevicePowerState);
    ASSERT(irpStack->Parameters.Power.State.DeviceState==PowerDeviceD0);

    deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
    KdPrint(("D12TEST.SYS:  enter D0 complete\n"));
    
    Irp->IoStatus.Status = ntStatus;

    D12_DecrementIoCount(deviceObject); 

    return ntStatus;
}


NTSTATUS
D12_SetDevicePowerState(
    IN PDEVICE_OBJECT DeviceObject,
    IN DEVICE_POWER_STATE DeviceState,
    IN PBOOLEAN HookIt
    )
/*++

Routine Description:

Arguments:

    DeviceObject - Pointer to the device object for the class device.

    Irp - Irp completed.

    DeviceState - Device specific power state to set the device in to.

Return Value:


--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    switch (DeviceState) {
    case PowerDeviceD3:

        //
        // device will be going OFF, save any state now.
        //

        D12_KdPrint(("D12TEST.SYS:  PowerDeviceD3 (OFF)\n"));

        KdPrint(("D12: PM power off\n"));
        deviceExtension->CurrentDevicePowerState = DeviceState;
        break;

    case PowerDeviceD1:
    case PowerDeviceD2:
        //
        // power states D1,D2 translate to USB suspend

        D12_KdPrint(("D12TEST.SYS:  PowerDeviceD1/D2 (SUSPEND)\n"));        

        KdPrint(("D12: device entered D%d\n", DeviceState-1));
        deviceExtension->CurrentDevicePowerState = DeviceState;
        break;

    case PowerDeviceD0:


        D12_KdPrint(("D12TEST.SYS:  PowerDeviceD0 (ON)\n"));

        //
        // finish the rest in the completion routine
        //

        *HookIt = TRUE;

        // pass on to PDO
        break;

    default:
        
        D12_KdPrint(("D12TEST.SYS:  Bogus DeviceState = %x\n", DeviceState));
    }

    return ntStatus;
}


NTSTATUS
D12_DeferIrpCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
/*++

Routine Description:

    This routine is called when the port driver completes an IRP.


Arguments:

    DeviceObject - Pointer to the device object for the class device.

    Irp - Irp completed.

    Context - Driver defined context.

Return Value:

    The function value is the final status from the operation.

--*/
{
    PKEVENT event = Context;


    KeSetEvent(event,
               1,
               FALSE);

    return STATUS_MORE_PROCESSING_REQUIRED;
    
}


NTSTATUS
D12_QueryCapabilities(
    IN PDEVICE_OBJECT PdoDeviceObject,
    IN PDEVICE_CAPABILITIES DeviceCapabilities
    )

/*++

Routine Description:

    This routine reads or write config space.

Arguments:

    DeviceObject        - Physical DeviceObject for this USB controller.

Return Value:

    None.

--*/

{
    PIO_STACK_LOCATION nextStack;
    PIRP irp;
    NTSTATUS ntStatus;
    KEVENT event;

    PAGED_CODE();
    irp = IoAllocateIrp(PdoDeviceObject->StackSize, FALSE);

    if (!irp) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    nextStack = IoGetNextIrpStackLocation(irp);
    ASSERT(nextStack != NULL);
    nextStack->MajorFunction= IRP_MJ_PNP;
    nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    IoSetCompletionRoutine(irp,
                           D12_DeferIrpCompletion,
                           &event,
                           TRUE,
                           TRUE,
                           TRUE);
                           
    //BUGBUG this is different from the latest version of busdd.doc
    nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;

    ntStatus = IoCallDriver(PdoDeviceObject,
                            irp);

    D12_KdPrint(("D12TEST.SYS:  ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));

    if (ntStatus == STATUS_PENDING) {
       // wait for irp to complete
       
       TRAP(); // first time we hit this
       
       KeWaitForSingleObject(
            &event,
            Suspended,
            KernelMode,
            FALSE,
            NULL);
    }

#if DBG                    
    if (!NT_SUCCESS(ntStatus)) {
        // failed? this is probably a bug
        D12_KdPrint(("D12TEST.SYS:  QueryCapabilities failed, why?\n"));
    }
#endif

    IoFreeIrp(irp);

    return STATUS_SUCCESS;
}


NTSTATUS
D12_ProcessPowerIrp(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
/*++

Routine Description:

    Process the Power IRPs sent to the PDO for this device.

    

Arguments:

    DeviceObject - pointer to a hcd device object (FDO)

    Irp          - pointer to an I/O Request Packet

Return Value:

    NT status code

--*/
{

    PIO_STACK_LOCATION irpStack;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    BOOLEAN hookIt = FALSE;

    D12_KdPrint(("D12TEST.SYS:  IRP_MJ_POWER\n"));

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    irpStack = IoGetCurrentIrpStackLocation (Irp);
    D12_IncrementIoCount(DeviceObject);
     
    switch (irpStack->MinorFunction) {
    case IRP_MN_WAIT_WAKE:
        D12_KdPrint(("D12TEST.SYS:  IRP_MN_WAIT_WAKE\n"));

        //
        // someone is enabling us for wakeup
        //
        TRAP();  // never seen this before?
        // pass this on to our PDO
        break;

    case IRP_MN_SET_POWER:
        {

        switch (irpStack->Parameters.Power.Type) {
        case SystemPowerState:
            //
            // find the device power state equivalent to the given system state
            //
        
            {
            POWER_STATE powerState;

            D12_KdPrint(("D12TEST.SYS:  Set Power, SystemPowerState (%d)\n", 
                irpStack->Parameters.Power.State.SystemState));                    

            if (irpStack->Parameters.Power.State.SystemState == 
                PowerSystemWorking) {
                                
                powerState.DeviceState = PowerDeviceD0;
            } else if (/* deviceExtension->EnabledForWakeup*/FALSE) {
            
                // BUGBUG for now act as if we are always enabled for wakeup
                D12_KdPrint(("D12TEST.SYS:  D12 always enabled for wakeup\n"));

                powerState.DeviceState =
                    deviceExtension->DeviceCapabilities.DeviceState[
                        irpStack->Parameters.Power.State.SystemState];

            } else {
                //
                // wakeup not enabled, just go in to the 'OFF' state.
                //
                powerState.DeviceState = PowerDeviceD3;
            } //irpStack->Parameters.Power.State.SystemState
            
            //
            // are we already in this state?
            //
            
            if (powerState.DeviceState != 
                deviceExtension->CurrentDevicePowerState) {
                
                // No,
                // request that we be put into this state
                deviceExtension->PowerIrp = Irp;
                ntStatus = PoRequestPowerIrp(deviceExtension->PhysicalDeviceObject,
                                           IRP_MN_SET_POWER,
                                           powerState,
                                           D12_PoRequestCompletion,
                                           DeviceObject,
                                           NULL);

            } else {
                // Yes,
                // just pass it on
                IoCopyCurrentIrpStackLocationToNext(Irp);
                PoStartNextPowerIrp(Irp);
                ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject,
                                        Irp);

                D12_DecrementIoCount(DeviceObject);                                        

            }

            } 
            break;

        case DevicePowerState:

            ntStatus = D12_SetDevicePowerState(DeviceObject,
                                                  irpStack->Parameters.Power.State.DeviceState,
                                                  &hookIt);

            IoCopyCurrentIrpStackLocationToNext(Irp);
            
            if (hookIt) {
                D12_KdPrint(("D12TEST.SYS:  Set PowerIrp Completion Routine\n"));
                IoSetCompletionRoutine(Irp,
                       D12_PowerIrp_Complete,
                       // always pass FDO to completion routine
                       DeviceObject,
                       hookIt,
                       hookIt,
                       hookIt);
            }

            PoStartNextPowerIrp(Irp);
            ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject,
                                    Irp);

            D12_DecrementIoCount(DeviceObject);
            
            break;
        } /* case irpStack->Parameters.Power.Type */

        } 
        break; /* IRP_MN_SET_POWER */

    case IRP_MN_QUERY_POWER:
    
        D12_KdPrint(("D12TEST.SYS:  IRP_MN_QUERY_POWER\n"));  

        IoCopyCurrentIrpStackLocationToNext(Irp);
        PoStartNextPowerIrp(Irp);
        ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject,
                                Irp);


        D12_DecrementIoCount(DeviceObject);

        break; /* IRP_MN_QUERY_POWER */            
    
    default:
    
        D12_KdPrint(("D12TEST.SYS:  UNKNOWN POWER MESSAGE (%x)\n", irpStack->MinorFunction));

        //
        // All unahndled PnP messages are passed on to the PDO
        //

        IoCopyCurrentIrpStackLocationToNext(Irp);

        //
        // All PNP_POWER POWER messages get passed to
        // TopOfStackDeviceObject and some are handled in the completion routine
        //

        // pass on to our PDO
        PoStartNextPowerIrp(Irp);
        ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject,
                                Irp);

        D12_DecrementIoCount(DeviceObject);                                

    } /* irpStack->MinorFunction */

    return ntStatus;
}


NTSTATUS
D12_Dispatch(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
/*++

Routine Description:

    Process the IRPs sent to this device.

Arguments:

    DeviceObject - pointer to a device object

    Irp          - pointer to an I/O Request Packet

Return Value:


--*/
{

    PIO_STACK_LOCATION irpStack;
    PDEVICE_EXTENSION deviceExtension;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_OBJECT stackDeviceObject;

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;

    //
    // Get a pointer to the current location in the Irp. This is where
    //     the function codes and parameters are located.
    //

    irpStack = IoGetCurrentIrpStackLocation (Irp);

    //
    // Get a pointer to the device extension
    //

    deviceExtension = DeviceObject->DeviceExtension;
    stackDeviceObject = deviceExtension->TopOfStackDeviceObject;

    D12_IncrementIoCount(DeviceObject);

    switch (irpStack->MajorFunction) {
    case IRP_MJ_SYSTEM_CONTROL: 
    
        D12_KdPrint (("D12TEST.SYS: IRP_MJ_SYSTEM_CONTROL\n"));
        IoCopyCurrentIrpStackLocationToNext(Irp);

        D12_KdPrint (("D12TEST.SYS: Passing SysCtrl Irp down\n"));

        D12_DecrementIoCount(DeviceObject);
        
        ntStatus = IoCallDriver(stackDeviceObject,
                                Irp);
        goto D12_Dispatch_Done;                                
        break;        

    case IRP_MJ_PNP:

        D12_KdPrint (("D12TEST.SYS: IRP_MJ_PNP\n"));

        switch (irpStack->MinorFunction) {
        case IRP_MN_START_DEVICE:

            {
            KEVENT event;
            D12_KdPrint (("D12TEST.SYS: IRP_MN_START_DEVICE\n"));
            
            KeInitializeEvent(&event, NotificationEvent, FALSE);

            IoCopyCurrentIrpStackLocationToNext(Irp);  
            IoSetCompletionRoutine(Irp,
                                   D12_DeferIrpCompletion,
                                   &event,
                                   TRUE,
                                   TRUE,
                                   TRUE);


            ntStatus = IoCallDriver(stackDeviceObject,
                                    Irp);
                
            if (ntStatus == STATUS_PENDING) {
                 // wait for irp to complete
           
                TRAP(); // first time we hit this
                       
                KeWaitForSingleObject(
                    &event,
                    Suspended,
                    KernelMode,
                    FALSE,
                    NULL);
            }

            ntStatus = D12_StartDevice(DeviceObject);
            Irp->IoStatus.Status = ntStatus;

            goto D12_Dispatch_CompleteRequest;
            }
            break;

        case IRP_MN_STOP_DEVICE:

            D12_KdPrint (("D12TEST.SYS: IRP_MN_STOP_DEVICE\n"));

            ntStatus = D12_StopDevice(DeviceObject);

            break;

        case IRP_MN_REMOVE_DEVICE:

            D12_KdPrint (("D12TEST.SYS: IRP_MN_REMOVE_DEVICE\n"));

            // match the inc at the begining of the dispatch
            // routine
            D12_DecrementIoCount(DeviceObject);

            //
            // ounce this flag is set no irps will be pased 
            // down the stack to lower drivers
            //
            deviceExtension->AcceptingRequests = FALSE;

            if (deviceExtension->UserEvent != NULL) {
                KeSetEvent(deviceExtension->UserEvent,
                           1,
                           FALSE);
            }

            if (NT_SUCCESS(ntStatus)) {
                LONG pendingIoCount;

                IoCopyCurrentIrpStackLocationToNext(Irp);  

                ntStatus = IoCallDriver(stackDeviceObject,
                                        Irp);

                //
                // final decrement will trigger the remove
                //
                pendingIoCount = D12_DecrementIoCount(DeviceObject);

                {
                    NTSTATUS status;

                    // wait for any io request pending in our driver to
                    // complete for finishing the remove

                    status = KeWaitForSingleObject(
                                &deviceExtension->RemoveEvent,
                                Suspended,
                                KernelMode,
                                FALSE,
                                NULL);

//                    TRAP();
                }
                //
                // Delete the link and FDO we created
                //

                D12_RemoveDevice(DeviceObject);

                D12_KdPrint (("D12TEST.SYS: Detaching from %08X\n",
                                 deviceExtension->TopOfStackDeviceObject));

                IoDetachDevice(deviceExtension->TopOfStackDeviceObject);

                D12_KdPrint (("D12TEST.SYS: Deleting %08X\n",
                                 DeviceObject));

                IoDeleteDevice (DeviceObject);

                goto D12_Dispatch_Done;
            }
            break;

        case IRP_MN_QUERY_STOP_DEVICE:
            D12_KdPrint (("D12TEST.SYS: IRP_MN_QUERY_STOP_DEVICE\n"));
            break;
        case IRP_MN_QUERY_REMOVE_DEVICE:
            D12_KdPrint (("D12TEST.SYS: IRP_MN_QUERY_REMOVE_DEVICE\n"));
            break;
        case IRP_MN_CANCEL_STOP_DEVICE:
            D12_KdPrint (("D12TEST.SYS: IRP_MN_CANCEL_STOP_DEVICE\n"));
            break;
        case IRP_MN_CANCEL_REMOVE_DEVICE:
            D12_KdPrint (("D12TEST.SYS: IRP_MN_CANCEL_REMOVE_DEVICE\n"));
            break;
        default:
            D12_KdPrint (("D12TEST.SYS: PnP IOCTL not handled\n"));
        } /* case MinorFunction, MajorFunction == IRP_MJ_PNP_POWER  */


        if (!NT_SUCCESS(ntStatus)) {
            Irp->IoStatus.Status = ntStatus;
            goto D12_Dispatch_CompleteRequest;
        }

        IoCopyCurrentIrpStackLocationToNext(Irp);

        //
        // All PNP_POWER messages get passed to the TopOfStackDeviceObject
        // we were given in PnPAddDevice
        //

        D12_KdPrint (("D12TEST.SYS: Passing PnP Irp down, status = %x\n", ntStatus));

        ntStatus = IoCallDriver(stackDeviceObject,
                                Irp);

        D12_DecrementIoCount(DeviceObject);

        goto D12_Dispatch_Done;
        break; // IRP_MJ_PNP

    default:
        D12_KdPrint (("D12TEST.SYS: MAJOR IOCTL not handled\n"));
        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;

    } /* case MajorFunction */


    ntStatus = Irp->IoStatus.Status;

D12_Dispatch_CompleteRequest:

    IoCompleteRequest (Irp,
                       IO_NO_INCREMENT
                       );

    D12_DecrementIoCount(DeviceObject);

D12_Dispatch_Done:

    D12_KdPrint (("D12TEST.SYS: Exit D12_Dispatch %x\n", ntStatus));

    return ntStatus;
}


VOID
D12_Unload(
    IN PDRIVER_OBJECT DriverObject
    )
/*++

Routine Description:

    Free all the allocated resources, etc.

Arguments:

    DriverObject - pointer to a driver object

Return Value:


--*/
{
    D12_KdPrint (("D12TEST.SYS: enter D12_Unload\n"));

    //
    // Free any global resources allocated
    // in DriverEntry
    //

    D12_KdPrint (("D12TEST.SYS: exit D12_Unload\n"));
}


NTSTATUS
D12_StartDevice(
    IN  PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Initializes a given instance of the device on the USB.
    All we do here is get the device descriptor and store it

Arguments:

    DeviceObject - pointer to the device object for this instance of a
                    82930

Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION deviceExtension;
    NTSTATUS ntStatus;
    PUSB_DEVICE_DESCRIPTOR deviceDescriptor = NULL;
    PURB urb;
    ULONG siz;

    D12_KdPrint (("D12TEST.SYS: enter D12_StartDevice\n"));

    deviceExtension = DeviceObject->DeviceExtension;
	
	D12_ResetIrpQueue(DeviceObject);

    urb = ExAllocatePool(NonPagedPool,
                         sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));

    if (urb) {

        siz = sizeof(USB_DEVICE_DESCRIPTOR);

        deviceDescriptor = ExAllocatePool(NonPagedPool,
                                          siz);

        if (deviceDescriptor) {

            UsbBuildGetDescriptorRequest(urb,
                                         (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                         USB_DEVICE_DESCRIPTOR_TYPE,
                                         0,
                                         0,
                                         deviceDescriptor,
                                         NULL,
                                         siz,
                                         NULL);

            ntStatus = D12_CallUSBD(DeviceObject, urb);

            if (NT_SUCCESS(ntStatus)) {
                D12_KdPrint (("D12TEST.SYS: Device Descriptor = %x, len %x\n",
                                deviceDescriptor,
                                urb->UrbControlDescriptorRequest.TransferBufferLength));

                D12_KdPrint (("D12TEST.SYS: D12 Device Descriptor:\n"));
                D12_KdPrint (("D12TEST.SYS: -------------------------\n"));
                D12_KdPrint (("D12TEST.SYS: bLength %d\n", deviceDescriptor->bLength));
                D12_KdPrint (("D12TEST.SYS: bDescriptorType 0x%x\n", deviceDescriptor->bDescriptorType));
                D12_KdPrint (("D12TEST.SYS: bcdUSB 0x%x\n", deviceDescriptor->bcdUSB));
                D12_KdPrint (("D12TEST.SYS: bDeviceClass 0x%x\n", deviceDescriptor->bDeviceClass));
                D12_KdPrint (("D12TEST.SYS: bDeviceSubClass 0x%x\n", deviceDescriptor->bDeviceSubClass));
                D12_KdPrint (("D12TEST.SYS: bDeviceProtocol 0x%x\n", deviceDescriptor->bDeviceProtocol));
                D12_KdPrint (("D12TEST.SYS: bMaxPacketSize0 0x%x\n", deviceDescriptor->bMaxPacketSize0));
                D12_KdPrint (("D12TEST.SYS: idVendor 0x%x\n", deviceDescriptor->idVendor));
                D12_KdPrint (("D12TEST.SYS: idProduct 0x%x\n", deviceDescriptor->idProduct));
                D12_KdPrint (("D12TEST.SYS: bcdDevice 0x%x\n", deviceDescriptor->bcdDevice));
                D12_KdPrint (("D12TEST.SYS: iManufacturer 0x%x\n", deviceDescriptor->iManufacturer));
                D12_KdPrint (("D12TEST.SYS: iProduct 0x%x\n", deviceDescriptor->iProduct));
                D12_KdPrint (("D12TEST.SYS: iSerialNumber 0x%x\n", deviceDescriptor->iSerialNumber));
                D12_KdPrint (("D12TEST.SYS: bNumConfigurations 0x%x\n", deviceDescriptor->bNumConfigurations));
            }
        } else {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }

        if (NT_SUCCESS(ntStatus)) {
            deviceExtension->DeviceDescriptor = deviceDescriptor;
        } else if (deviceDescriptor) {
            ExFreePool(deviceDescriptor);
        }

        ExFreePool(urb);

    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    if (NT_SUCCESS(ntStatus)) {
        ntStatus = D12_ConfigureDevice(DeviceObject);
    }

    if (NT_SUCCESS(ntStatus)) {
        ntStatus = D12_BuildPipeList(DeviceObject);
    }

    D12_KdPrint (("D12TEST.SYS: exit D12_StartDevice (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
D12_RemoveDevice(
    IN  PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Stops a given instance of a 82930 device on the USB.

Arguments:

    DeviceObject - pointer to the device object for this instance of a 82930

Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION deviceExtension;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UNICODE_STRING deviceLinkUnicodeString;

    D12_KdPrint (("D12TEST.SYS: enter D12_RemoveDevice\n"));

    deviceExtension = DeviceObject->DeviceExtension;

    
    RtlInitUnicodeString (&deviceLinkUnicodeString,
                          deviceExtension->DeviceLinkNameBuffer);

    // remove the GUID-based symbolic link
    ntStatus = IoSetDeviceInterfaceState(&deviceLinkUnicodeString, FALSE);

    //
    // Free device descriptor structure
    //

    if (deviceExtension->DeviceDescriptor) {
        ExFreePool(deviceExtension->DeviceDescriptor);
    }

    //
    // Free up any interface structures
    //

    if (deviceExtension->Interface) {
        ExFreePool(deviceExtension->Interface);
    }

    D12_KdPrint (("D12TEST.SYS: exit D12_RemoveDevice (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
D12_StopDevice(
    IN  PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Stops a given instance of a 82930 device on the USB, this is only
    stuff we need to do if the device is still present.

Arguments:

    DeviceObject - pointer to the device object for this instance of a 82930

Return Value:

    NT status code

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PURB urb;
    ULONG siz;

    D12_KdPrint (("D12TEST.SYS: enter D12_StopDevice\n"));

	D12_CancelAllPendingIrps(DeviceObject);

    //
    // Send the select configuration urb with a NULL pointer for the configuration
    // handle, this closes the configuration and puts the device in the 'unconfigured'
    // state.
    //

    siz = sizeof(struct _URB_SELECT_CONFIGURATION);

    urb = ExAllocatePool(NonPagedPool,
                         siz);

    if (urb) {
        NTSTATUS status;

        UsbBuildSelectConfigurationRequest(urb,
                                          (USHORT) siz,
                                          NULL);

        status = D12_CallUSBD(DeviceObject, urb);

        D12_KdPrint (("D12TEST.SYS: Device Configuration Closed status = %x usb status = %x.\n",
                        status, urb->UrbHeader.Status));

        ExFreePool(urb);
    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    D12_KdPrint (("D12TEST.SYS: exit D12_StopDevice (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
D12_PnPAddDevice(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PhysicalDeviceObject
    )
/*++

Routine Description:

    This routine is called to create a new instance of the device

Arguments:

    DriverObject - pointer to the driver object for this instance of D12

    PhysicalDeviceObject - pointer to a device object created by the bus

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS                ntStatus = STATUS_SUCCESS;
    PDEVICE_OBJECT          deviceObject = NULL;
    PDEVICE_EXTENSION       deviceExtension;
    USBD_VERSION_INFORMATION versionInformation;
    static ULONG instance = 0;

    D12_KdPrint (("D12TEST.SYS: enter D12_PnPAddDevice\n"));

    //
    // create our funtional device object (FDO)
    //

    ntStatus =
        BulkUsb_CreateDeviceObject(DriverObject, PhysicalDeviceObject, &deviceObject);

    if (NT_SUCCESS(ntStatus)) {
        deviceExtension = deviceObject->DeviceExtension;

        deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

        //
        // we support direct io for read/write
        //
        deviceObject->Flags |= DO_DIRECT_IO;


        //** initialize our device extension
        //
        // remember the Physical device Object
        //
        deviceExtension->PhysicalDeviceObject=PhysicalDeviceObject;

        //
        // Attach to the PDO
        //

        deviceExtension->TopOfStackDeviceObject =
            IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);

        D12_QueryCapabilities(PhysicalDeviceObject,
                                 &deviceExtension->DeviceCapabilities);            

        //
        // display the device  caps
        //
#if DBG
        {
        ULONG i;
        
        D12_KdPrint(("D12TEST.SYS:  >>>>>> DeviceCaps\n"));  
        D12_KdPrint(("D12TEST.SYS:  SystemWake = (%d)\n", 
            deviceExtension->DeviceCapabilities.SystemWake));    
        D12_KdPrint(("D12TEST.SYS:  DeviceWake = (D%d)\n",
            deviceExtension->DeviceCapabilities.DeviceWake-1));

        for (i=PowerSystemUnspecified; i< PowerSystemMaximum; i++) {
            
            D12_KdPrint(("D12TEST.SYS:  Device State Map: sysstate %d = devstate 0x%x\n", i, 
                 deviceExtension->DeviceCapabilities.DeviceState[i]));       
        }
        D12_KdPrint(("D12TEST.SYS:  '<<<<<<<<DeviceCaps\n"));
        }
#endif
        //
        // transition to zero signals the event
        //
        D12_IncrementIoCount(deviceObject);                                 
    }

    USBD_GetUSBDIVersion(&versionInformation);

    D12_KdPrint (("D12TEST.SYS: exit D12_PnPAddDevice (%x)\n", ntStatus));

    return ntStatus;
}

NTSTATUS
BulkUsb_SymbolicLink(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PUNICODE_STRING deviceLinkUnicodeString

    )
/*++

Routine Description:

    This routine is called to create and initialize
    a GUID-based symbolic link to our device to be used to open/create 
    instances of us from user mode.

    Called from BulkUsb_CreateDeviceObject() to create the link. 

Arguments:

    DeviceObject - pointer to our Physical Device Object ( PDO )

    deviceLinkUnicodeString - Points to a unicode string structure allocated by the caller. 
        If this routine is successful, it initializes the unicode string and allocates 
        the string buffer containing the kernel-mode path to the symbolic link for this 
        device interface. 


Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/{
    NTSTATUS ntStatus = STATUS_SUCCESS;


    //  Create the symbolic link
     
    // IoRegisterDeviceInterface registers device functionality (a device interface) 
    // that a driver will enable for use by applications or other system components.

    ntStatus = IoRegisterDeviceInterface(
                DeviceObject,
                (LPGUID)&GUID_CLASS_D12_BULK,
                NULL,
                deviceLinkUnicodeString);

   if (NT_SUCCESS(ntStatus)) {

       // IoSetDeviceInterfaceState enables or disables a previously 
       // registered device interface. Applications and other system components 
       // can open only interfaces that are enabled.

        ntStatus = IoSetDeviceInterfaceState(deviceLinkUnicodeString, TRUE);


    }

    return ntStatus;
}



NTSTATUS
BulkUsb_CreateDeviceObject(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PhysicalDeviceObject,
    IN PDEVICE_OBJECT *DeviceObject
    )
/*++

Routine Description:

    Creates a Functional DeviceObject

Arguments:

    DriverObject - pointer to the driver object for device

    DeviceObject - pointer to DeviceObject pointer to return
                    created device object.

    Instance - instance of the device create.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus;
    UNICODE_STRING deviceLinkUnicodeString;
    PDEVICE_EXTENSION deviceExtension;
    USHORT i;


    ntStatus = BulkUsb_SymbolicLink( PhysicalDeviceObject, &deviceLinkUnicodeString );


    if (NT_SUCCESS(ntStatus)) {

        ntStatus = IoCreateDevice (DriverObject,
                           sizeof (DEVICE_EXTENSION),
                           NULL,
                           FILE_DEVICE_UNKNOWN,
                           FILE_AUTOGENERATED_DEVICE_NAME,
                           FALSE,
                           DeviceObject);

 
        if (!NT_SUCCESS(ntStatus))  {
             return ntStatus;
        }


        // Name buffer for our named Functional device object link
        // The name is generated based on the driver's class GUID
        deviceExtension = (PDEVICE_EXTENSION) ((*DeviceObject)->DeviceExtension);

        RtlCopyMemory(deviceExtension->DeviceLinkNameBuffer,
                      deviceLinkUnicodeString.Buffer,
                      deviceLinkUnicodeString.Length);
        deviceExtension->DeviceDescriptor = NULL;
        deviceExtension->Interface = NULL;
        deviceExtension->ConfigurationHandle = NULL;
        deviceExtension->AcceptingRequests = TRUE;
        deviceExtension->PendingIoCount = 0;
        deviceExtension->UserEvent = NULL;

        KeInitializeEvent(&deviceExtension->RemoveEvent, NotificationEvent, FALSE);

        for (i=0; i<D12_MAX_PIPES; i++) {
            deviceExtension->PipeList[i].PipeInfo = NULL;
        }

		RtlFreeUnicodeString( &deviceLinkUnicodeString );
    }


    return ntStatus;
}



NTSTATUS
D12_CallUSBD(
    IN PDEVICE_OBJECT DeviceObject,
    IN PURB Urb
    )
/*++

Routine Description:

    Passes a URB to the USBD class driver

Arguments:

    DeviceObject - pointer to the device object for this instance of an 82930

    Urb - pointer to Urb request block

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus, status = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    PIRP irp;
    KEVENT event;
    IO_STATUS_BLOCK ioStatus;
    PIO_STACK_LOCATION nextStack;

    D12_KdPrint (("D12TEST.SYS: enter D12_CallUSBD\n"));

    deviceExtension = DeviceObject->DeviceExtension;

    //
    // issue a synchronous request
    //

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    irp = IoBuildDeviceIoControlRequest(
                IOCTL_INTERNAL_USB_SUBMIT_URB,
                deviceExtension->TopOfStackDeviceObject,
                NULL,
                0,
                NULL,
                0,
                TRUE, /* INTERNAL */
                &event,
                &ioStatus);

    //
    // Call the class driver to perform the operation.  If the returned status
    // is PENDING, wait for the request to complete.
    //

    nextStack = IoGetNextIrpStackLocation(irp);
    ASSERT(nextStack != NULL);

    //
    // pass the URB to the USB driver stack
    //
    nextStack->Parameters.Others.Argument1 = Urb;


    D12_KdPrint (("D12TEST.SYS: calling USBD\n"));

    ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,
                            irp);

    D12_KdPrint (("D12TEST.SYS: return from IoCallDriver USBD %x\n", ntStatus));

    if (ntStatus == STATUS_PENDING) {
        D12_KdPrint (("D12TEST.SYS: Wait for single object\n"));

        status = KeWaitForSingleObject(
                       &event,
                       Suspended,
                       KernelMode,
                       FALSE,
                       NULL);

        D12_KdPrint (("D12TEST.SYS: Wait for single object, returned %x\n", status));

    } else {
        ioStatus.Status = ntStatus;
    }

    D12_KdPrint (("D12TEST.SYS: URB status = %x status = %x irp status %x\n",
        Urb->UrbHeader.Status, status, ioStatus.Status));

    //
    // USBD maps the error code for us
    //
    ntStatus = ioStatus.Status;

    D12_KdPrint (("D12TEST.SYS: exit D12_CallUSBD (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
D12_ConfigureDevice(
    IN  PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Initializes a given instance of the device on the USB and selects the
    configuration.

Arguments:

    DeviceObject - pointer to the device object for this instance of the 82930
                    devcice.


Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION deviceExtension;
    NTSTATUS ntStatus;
    PURB urb;
    ULONG siz;
    PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL;

    D12_KdPrint (("D12TEST.SYS: enter D12_ConfigureDevice\n"));

    deviceExtension = DeviceObject->DeviceExtension;

    //
    // first configure the device
    //

    urb = ExAllocatePool(NonPagedPool,
                         sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));

    if (urb) {

        // BUGBUG 82930 chokes if on the next command if you don't get
        // the entire descriptor on the first try

        siz = sizeof(USB_CONFIGURATION_DESCRIPTOR)+256;

get_config_descriptor_retry:

        configurationDescriptor = ExAllocatePool(NonPagedPool,
                                                 siz);

        if (configurationDescriptor) {

            UsbBuildGetDescriptorRequest(urb,
                                         (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                         USB_CONFIGURATION_DESCRIPTOR_TYPE,
                                         0,
                                         0,
                                         configurationDescriptor,
                                         NULL,
                                         siz,
                                         NULL);

            ntStatus = D12_CallUSBD(DeviceObject, urb);

            D12_KdPrint (("D12TEST.SYS: Configuration Descriptor = %x, len %x\n",
                            configurationDescriptor,
                            urb->UrbControlDescriptorRequest.TransferBufferLength));
        } else {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }

        //
        // if we got some data see if it was enough.
        //
        // NOTE: we may get an error in URB because of buffer overrun
        if (urb->UrbControlDescriptorRequest.TransferBufferLength>0 &&
                configurationDescriptor->wTotalLength > siz) {

            siz = configurationDescriptor->wTotalLength;
            ExFreePool(configurationDescriptor);
            configurationDescriptor = NULL;
            goto get_config_descriptor_retry;
        }

        ExFreePool(urb);

    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    if (configurationDescriptor) {

        //
        // We have the configuration descriptor for the configuration
        // we want.
        //
        // Now we issue the select configuration command to get
        // the  pipes associated with this configuration.
        //

        ntStatus = D12_SelectInterface(DeviceObject,
            configurationDescriptor, NULL);

        ExFreePool(configurationDescriptor);

    }



    D12_KdPrint (("D12TEST.SYS: exit D12_ConfigureDevice (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
D12_SelectInterface(
    IN PDEVICE_OBJECT DeviceObject,
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
    PUSBD_INTERFACE_INFORMATION Interface
    )
/*++

Routine Description:

    Initializes an 82930 with multiple interfaces

Arguments:

    DeviceObject - pointer to the device object for this instance of the 82930
                    devcice.

    ConfigurationDescriptor - pointer to the USB configuration
                    descriptor containing the interface and endpoint
                    descriptors.

Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION deviceExtension;
    NTSTATUS ntStatus;
    PURB urb = NULL;
    ULONG i;
    UCHAR alternateSetting, interfaceNumber;
    PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor = NULL;
    USHORT siz;

    D12_KdPrint (("D12TEST.SYS: enter D12_SelectInterface\n"));

    deviceExtension = DeviceObject->DeviceExtension;

    if (Interface == NULL) {

        //
        // D12 driver only supports one interface, we must search
        // the configuration descriptor for the interface we want
        // and remember the pipes.
        //

        urb = USBD_CreateConfigurationRequest(ConfigurationDescriptor, &siz);

        if (urb) {

            //
            // search thru all the interfaces
            // and find any we are interested in
            //

            interfaceNumber = 0;
            alternateSetting = 0;
//            alternateSetting = 3;

            interfaceDescriptor =
                USBD_ParseConfigurationDescriptor(ConfigurationDescriptor,
                                                  interfaceNumber,
                                                  alternateSetting);

            Interface = &urb->UrbSelectConfiguration.Interface;
            
            for (i=0; i< Interface->NumberOfPipes; i++) {
                //
                // perform any pipe initialization here
                //
                Interface->Pipes[i].MaximumTransferSize = 64*1024-1;
            }

            UsbBuildSelectConfigurationRequest(urb,
                                              (USHORT) siz,
                                              ConfigurationDescriptor);

//            Interface->AlternateSetting = 3;
            
            ntStatus = D12_CallUSBD(DeviceObject, urb);

            deviceExtension->ConfigurationHandle =
                urb->UrbSelectConfiguration.ConfigurationHandle;

        } else {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }

    } else {
        //
        // we were give an interface already set up
        //
        // do a selectinterface here if we want to change any of the
        // pipe parameters.
        //

        TRAP();

    }

    ASSERT(Interface != NULL);

    if (NT_SUCCESS(ntStatus)) {

        //
        // Save the configuration handle for this device
        //

        deviceExtension->ConfigurationHandle =
            urb->UrbSelectConfiguration.ConfigurationHandle;

        deviceExtension->Interface = ExAllocatePool(NonPagedPool,
                                                    Interface->Length);

        if (deviceExtension->Interface) {
            ULONG j;

            //
            // save a copy of the interface information returned
            //
            RtlCopyMemory(deviceExtension->Interface, Interface, Interface->Length);

            //
            // Dump the interface to the debugger
            //
            D12_KdPrint (("D12TEST.SYS: ---------\n"));
            D12_KdPrint (("D12TEST.SYS: NumberOfPipes 0x%x\n", deviceExtension->Interface->NumberOfPipes));
            D12_KdPrint (("D12TEST.SYS: Length 0x%x\n", deviceExtension->Interface->Length));
            D12_KdPrint (("D12TEST.SYS: Alt Setting 0x%x\n", deviceExtension->Interface->AlternateSetting));
            D12_KdPrint (("D12TEST.SYS: Interface Number 0x%x\n", deviceExtension->Interface->InterfaceNumber));
            D12_KdPrint (("D12TEST.SYS: Class, subclass, protocol 0x%x 0x%x 0x%x\n",
                deviceExtension->Interface->Class,
                deviceExtension->Interface->SubClass,
                deviceExtension->Interface->Protocol));

            // Dump the pipe info

            for (j=0; j<Interface->NumberOfPipes; j++) {
                PUSBD_PIPE_INFORMATION pipeInformation;

                pipeInformation = &deviceExtension->Interface->Pipes[j];

                D12_KdPrint (("D12TEST.SYS: ---------\n"));
                D12_KdPrint (("D12TEST.SYS: PipeType 0x%x\n", pipeInformation->PipeType));
                D12_KdPrint (("D12TEST.SYS: EndpointAddress 0x%x\n", pipeInformation->EndpointAddress));
                D12_KdPrint (("D12TEST.SYS: MaxPacketSize 0x%x\n", pipeInformation->MaximumPacketSize));
                D12_KdPrint (("D12TEST.SYS: Interval 0x%x\n", pipeInformation->Interval));
                D12_KdPrint (("D12TEST.SYS: Handle 0x%x\n", pipeInformation->PipeHandle));
                D12_KdPrint (("D12TEST.SYS: MaximumTransferSize 0x%x\n", pipeInformation->MaximumTransferSize));
            }

            D12_KdPrint (("D12TEST.SYS: ---------\n"));
        }
    }

    if (urb) {
        ExFreePool(urb);
        urb = NULL;
    }

    // Retrieve the selected Configuration and Interface setting from the
    // device.  (The only purpose of doing this here is to exercise the
    // URB_FUNCTION_GET_CONFIGURATION and URB_FUNCTION_GET_INTERFACE
    // requests).
    //
    if (NT_SUCCESS(ntStatus)) {

        urb = ExAllocatePool(
                  NonPagedPool,
                  sizeof(struct _URB_CONTROL_GET_CONFIGURATION_REQUEST) + 1);

        if (urb)
        {
            PUCHAR configuration;

            configuration = (PUCHAR)urb + sizeof(struct _URB_CONTROL_GET_CONFIGURATION_REQUEST);
            *configuration = 0xFF;

            urb->UrbHeader.Function = URB_FUNCTION_GET_CONFIGURATION;
            urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_GET_CONFIGURATION_REQUEST);
            urb->UrbControlGetConfigurationRequest.TransferBufferLength = 1;
            urb->UrbControlGetConfigurationRequest.TransferBuffer = configuration;
            urb->UrbControlGetConfigurationRequest.TransferBufferMDL = NULL;
            urb->UrbControlGetConfigurationRequest.UrbLink = NULL;

            ntStatus = D12_CallUSBD(DeviceObject, urb);

            D12_KdPrint (("D12TEST.SYS: Configuration %d (%x)\n",
                             *configuration, ntStatus));

            ExFreePool(urb);
            urb = NULL;
        }

        urb = ExAllocatePool(
                  NonPagedPool,
                  sizeof(struct _URB_CONTROL_GET_INTERFACE_REQUEST) + 1);

        if (urb)
        {
            PUCHAR interface;

            interface = (PUCHAR)urb + sizeof(struct _URB_CONTROL_GET_INTERFACE_REQUEST);
            *interface = 0xFF;

            urb->UrbHeader.Function = URB_FUNCTION_GET_INTERFACE;
            urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_GET_INTERFACE_REQUEST);
            urb->UrbControlGetInterfaceRequest.TransferBufferLength = 1;
            urb->UrbControlGetInterfaceRequest.TransferBuffer = interface;
            urb->UrbControlGetInterfaceRequest.TransferBufferMDL = NULL;
            urb->UrbControlGetInterfaceRequest.UrbLink = NULL;
            urb->UrbControlGetInterfaceRequest.Interface =
                deviceExtension->Interface->InterfaceNumber;

            ntStatus = D12_CallUSBD(DeviceObject, urb);

            D12_KdPrint (("D12TEST.SYS: Interface %d (%x)\n",
                             *interface, ntStatus));

            ExFreePool(urb);
            urb = NULL;
        }
    }

    D12_KdPrint (("D12TEST.SYS: exit D12_SelectInterface (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
D12_BuildPipeList(
    IN  PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

Arguments:

    DeviceObject - pointer to the device object for this instance of the 82930
                    devcice.


Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION deviceExtension;
    ULONG i;
    WCHAR Name[] = L"\\PIPE00";
    PUSBD_INTERFACE_INFORMATION interface;

    deviceExtension = DeviceObject->DeviceExtension;
    interface = deviceExtension->Interface;

    D12_KdPrint (("D12TEST.SYS: enter D12_BuildPipeList\n"));

    deviceExtension = DeviceObject->DeviceExtension;

    for (i=0; i<D12_MAX_PIPES; i++) {

        Name[6] = 'X';
        RtlCopyMemory(deviceExtension->PipeList[i].Name,
                      Name,
                      sizeof(Name));

    }

    //
    // build a list of pipe names based on the interface
    //

    for (i=0; i<interface->NumberOfPipes; i++) {

        Name[6] = '0' + (USHORT) i;
        RtlCopyMemory(deviceExtension->PipeList[i].Name,
                      Name,
                      sizeof(Name));

        deviceExtension->PipeList[i].PipeInfo =
            &interface->Pipes[i];

        deviceExtension->PipeList[i].Opened = FALSE;
    }

    return STATUS_SUCCESS;
}


NTSTATUS
D12_ResetPipe(
    IN PDEVICE_OBJECT DeviceObject,
    IN PD12_PIPE Pipe,
    IN BOOLEAN IsoClearStall
    )
/*++

Routine Description:

    Reset a given USB pipe.
    
    NOTES:

    This will reset the host to Data0 and should also reset the device
    to Data0 for Bulk and Interrupt pipes.

    For Iso pipes this will set the virgin state of pipe so that ASAP
    transfers begin with the current bus frame instead of the next frame
    after the last transfer occurred.

Arguments:

Return Value:


--*/
{
    NTSTATUS ntStatus;
    PURB urb;

    D12_KdPrint (("D12TEST.SYS: Reset Pipe %x\n", Pipe));

    urb = ExAllocatePool(NonPagedPool,
                         sizeof(struct _URB_PIPE_REQUEST));

    if (urb) {

        urb->UrbHeader.Length = (USHORT) sizeof (struct _URB_PIPE_REQUEST);
        urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
        urb->UrbPipeRequest.PipeHandle =
            Pipe->PipeInfo->PipeHandle;

        ntStatus = D12_CallUSBD(DeviceObject, urb);

        ExFreePool(urb);

    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Memphis RESET_PIPE will send a Clear-Feature Endpoint Stall to
    // reset the data toggle of non-Iso pipes as part of a RESET_PIPE
    // request.  It does not do this for Iso pipes as Iso pipes do not use
    // the data toggle (all Iso packets are Data0).  However, we also use
    // the Clear-Feature Endpoint Stall request in our device firmware to
    // reset data buffer points inside the device so we explicitly send
    // this request to the device for Iso pipes if desired.
    //
    if (NT_SUCCESS(ntStatus) && IsoClearStall &&
        (Pipe->PipeInfo->PipeType == UsbdPipeTypeIsochronous)) {
        
        urb = ExAllocatePool(NonPagedPool,
                             sizeof(struct _URB_CONTROL_FEATURE_REQUEST));

        if (urb) {

            UsbBuildFeatureRequest(urb,
                                   URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT,
                                   USB_FEATURE_ENDPOINT_STALL,
                                   Pipe->PipeInfo->EndpointAddress,
                                   NULL);

            ntStatus = D12_CallUSBD(DeviceObject, urb);

            ExFreePool(urb);
        } else {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    return ntStatus;
}


LONG
D12_DecrementIoCount(
    IN PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

Arguments:

Return Value:


--*/
{
    PDEVICE_EXTENSION deviceExtension;
    LONG ioCount;

    deviceExtension = DeviceObject->DeviceExtension;

    ioCount = InterlockedDecrement(&deviceExtension->PendingIoCount);

    D12_KdPrint (("D12TEST.SYS: Pending io count = %x\n", ioCount));

    if (ioCount==0) {
        KeSetEvent(&deviceExtension->RemoveEvent,
                   1,
                   FALSE);
    }

    return ioCount;
}


VOID
D12_IncrementIoCount(
    IN PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

Arguments:

Return Value:


--*/
{
    PDEVICE_EXTENSION deviceExtension;

    deviceExtension = DeviceObject->DeviceExtension;

    InterlockedIncrement(&deviceExtension->PendingIoCount);
}


NTSTATUS
D12_ReconfigureDevice(
    IN  PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Initializes a given instance of the device on the USB and selects the
    configuration.

Arguments:

    DeviceObject - pointer to the device object for this instance of the 82930
                    devcice.


Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION deviceExtension;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PUSBD_INTERFACE_INFORMATION interface;
    ULONG i;

    D12_KdPrint (("D12TEST.SYS: enter D12_ReconfigureDevice\n"));

    deviceExtension = DeviceObject->DeviceExtension;

    if (NT_SUCCESS(ntStatus)) {
        ntStatus = D12_ConfigureDevice(DeviceObject);
    }

    //
    // new interface structure is now set up
    //

    interface = deviceExtension->Interface;

    //
    // set up the pipe handles again
    //

    for (i=0; i<interface->NumberOfPipes; i++) {

        D12_KdPrint (("D12TEST.SYS: pipe list = %x\n", &deviceExtension->PipeList[i]));

        deviceExtension->PipeList[i].PipeInfo =
            &interface->Pipes[i];

        //deviceExtension->PipeList[i].Opened = FALSE;
    }

    return ntStatus;
}

